package com.ruoyi.framework.aspect;

import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.entity.BaseEntity;
import com.ruoyi.common.entity.LoginUser;
import com.ruoyi.common.entity.system.SysRole;
import com.ruoyi.common.entity.system.SysUser;
import com.ruoyi.common.enums.ExceptionEnum;
import com.ruoyi.common.exception.BizException;
import com.ruoyi.common.util.ServletUtils;
import com.ruoyi.common.util.bean.SpringUtils;
import com.ruoyi.framework.service.TokenService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Optional;

/**
 * @author LiuXiaoYu
 * @date 2021/10/11- 20:53
 */
@Component
@Aspect
@Slf4j
@Order(3)
public class DataScopeAspect{


    /**
     * 全部数据权限
     */
    public static final String DATA_SCOPE_ALL = "1";

    /**
     * 自定数据权限
     */
    public static final String DATA_SCOPE_CUSTOM = "2";

    /**
     * 部门数据权限
     */
    public static final String DATA_SCOPE_DEPT = "3";

    /**
     * 部门及以下数据权限
     */
    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";

    /**
     * 仅本人数据权限
     */
    public static final String DATA_SCOPE_SELF = "5";

    /**
     * 数据权限过滤关键字
     */
    public static final String DATA_SCOPE = "dataScope";
    /**
     * 配置切入点
     */
    @Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)")
    public void dataScopeFilter(){}

    @Before("dataScopeFilter()")
    public void before(JoinPoint point){
        //防止sql参数注入 先清空
        clearDataScope(point);
        //数据权限过滤
        handleDataScope(point);
    }

    /**
     * 预备数据权限过滤
     * @param point
     */
    private void handleDataScope(JoinPoint point) {
        DataScope dataScopeAnnotation = getDataScopeAnnotation(point);
        if (Objects.isNull(dataScopeAnnotation)){
            return;
        }
//        Optional.ofNullable(SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest()))
//                .map(LoginUser::getUser)
//                .filter(sysUser -> !sysUser.isAdmin())
//                .map(sysUser->handleDataScopeFilter(point,sysUser,dataScopeAnnotation.deptAlias(),dataScopeAnnotation.userAlias()))
//                .orElseThrow(()->new BizException("数据权限切面报错,Optional出现空指针"));
        //获取当前登录用户
        LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
        SysUser user = loginUser.getUser();
        //不是超级管理员就要进行数据权限过滤
        if(Objects.nonNull(user)&&!user.isAdmin()){
            handleDataScopeFilter(point,user,dataScopeAnnotation.deptAlias(),dataScopeAnnotation.userAlias());
        }

    }

    /**
     * 正式进行数据权限过滤
     * @param point 切入点
     * @param user 当前登录用户
     * @param deptAlias 部门表别名
     * @param userAlias 用户表别名
     */
    private void handleDataScopeFilter(JoinPoint point, SysUser user, String deptAlias, String userAlias) {
            StringBuilder sqlFilter=new StringBuilder();
            for (SysRole role:user.getRoles()){
              //获取角色的数据权限
                switch (role.getDataScope()){
                    case DATA_SCOPE_ALL:
                        sqlFilter=new StringBuilder();
                        break;
                    case DATA_SCOPE_CUSTOM:
                        sqlFilter.append("OR ").append(deptAlias)
                                .append(".dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = ")
                                .append(role.getRoleId()).append(")");
                        break;
                    case DATA_SCOPE_DEPT:
                        sqlFilter.append("OR ").append(deptAlias).append(".dept_id = ").append(user.getDeptId());
                        break;
                    case DATA_SCOPE_DEPT_AND_CHILD:
                        sqlFilter.append("OR ").append(deptAlias).append(".dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = ")
                                .append(user.getDeptId()).append(" or find_in_set(").append(user.getDeptId()).append(", ancestors ) )");
                        break;
                    case DATA_SCOPE_SELF:
                        if (StringUtils.isNotBlank(userAlias)){
                            sqlFilter.append("OR ").append(userAlias).append(".user_id = ").append(user.getUserId());
                            break;
                        }else {
                            //数据权限为仅本人且没有userAlias别名就不查询任何数据
                            sqlFilter.append("OR 1=0");
                            break;
                        }
                    default:
                        throw new BizException(ExceptionEnum.NO_SET_DATASCOPE,"未设置数据权限");
                }
            }
        if (StringUtils.isNotBlank(sqlFilter.toString())) {
            Object params = point.getArgs()[0];
            if (Objects.nonNull(params) && params instanceof BaseEntity) {
                BaseEntity baseEntity = (BaseEntity) params;
               // log.info("----------------sqlFilter:{}",sqlFilter);
               // log.info("********************sqlFilter:{}","AND (" + sqlFilter.substring(3) + ")");
                baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlFilter.substring(3) + ")");
            }
        }
    }

    /**
     * 是否存在该注解 存在就获取
     * @param point
     * @return
     */
    private DataScope getDataScopeAnnotation(JoinPoint point) {
        Signature signature = point.getSignature();
        MethodSignature methodSignature=(MethodSignature)signature;
        Method method = methodSignature.getMethod();
        if(Objects.nonNull(method)){
            return method.getAnnotation(DataScope.class);
        }
        return null;
    }

    /**
     * 拼接权限sql前先清空params.dataScope参数防止注入
     * @param point
     */
    private void clearDataScope(JoinPoint point) {
        Object param= point.getArgs()[0];
        if (Objects.nonNull(param) && param instanceof BaseEntity){
            BaseEntity baseEntity=(BaseEntity)param;
            baseEntity.getParams().put(DATA_SCOPE,"");
        }

    }

}

